1   /*
2    * Copyright (C) 2008 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  
19  import static com.google.common.base.Preconditions.checkNotNull;
20  
21  import com.google.common.annotations.GwtCompatible;
22  import com.google.common.collect.Multiset.Entry;
23  import com.google.common.primitives.Ints;
24  
25  import java.io.Serializable;
26  import java.util.Arrays;
27  import java.util.Collection;
28  import java.util.Iterator;
29  
30  import javax.annotation.Nullable;
31  
32  /**
33   * An immutable hash-based multiset. Does not permit null elements.
34   *
35   * <p>Its iterator orders elements according to the first appearance of the
36   * element among the items passed to the factory method or builder. When the
37   * multiset contains multiple instances of an element, those instances are
38   * consecutive in the iteration order.
39   *
40   * <p>See the Guava User Guide article on <a href=
41   * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained">
42   * immutable collections</a>.
43   *
44   * @author Jared Levy
45   * @author Louis Wasserman
46   * @since 2.0 (imported from Google Collections Library)
47   */
48  @GwtCompatible(serializable = true, emulated = true)
49  @SuppressWarnings("serial") // we're overriding default serialization
50  // TODO(user): write an efficient asList() implementation
51  public abstract class ImmutableMultiset<E> extends ImmutableCollection<E>
52      implements Multiset<E> {
53  
54    private static final ImmutableMultiset<Object> EMPTY =
55        new RegularImmutableMultiset<Object>(ImmutableMap.<Object, Integer>of(), 0);
56  
57    /**
58     * Returns the empty immutable multiset.
59     */
60    @SuppressWarnings("unchecked") // all supported methods are covariant
61    public static <E> ImmutableMultiset<E> of() {
62      return (ImmutableMultiset<E>) EMPTY;
63    }
64  
65    /**
66     * Returns an immutable multiset containing a single element.
67     *
68     * @throws NullPointerException if {@code element} is null
69     * @since 6.0 (source-compatible since 2.0)
70     */
71    @SuppressWarnings("unchecked") // generic array created but never written
72    public static <E> ImmutableMultiset<E> of(E element) {
73      return copyOfInternal(element);
74    }
75  
76    /**
77     * Returns an immutable multiset containing the given elements, in order.
78     *
79     * @throws NullPointerException if any element is null
80     * @since 6.0 (source-compatible since 2.0)
81     */
82    @SuppressWarnings("unchecked") //
83    public static <E> ImmutableMultiset<E> of(E e1, E e2) {
84      return copyOfInternal(e1, e2);
85    }
86  
87    /**
88     * Returns an immutable multiset containing the given elements, in order.
89     *
90     * @throws NullPointerException if any element is null
91     * @since 6.0 (source-compatible since 2.0)
92     */
93    @SuppressWarnings("unchecked") //
94    public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3) {
95      return copyOfInternal(e1, e2, e3);
96    }
97  
98    /**
99     * Returns an immutable multiset containing the given elements, in order.
100    *
101    * @throws NullPointerException if any element is null
102    * @since 6.0 (source-compatible since 2.0)
103    */
104   @SuppressWarnings("unchecked") //
105   public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4) {
106     return copyOfInternal(e1, e2, e3, e4);
107   }
108 
109   /**
110    * Returns an immutable multiset containing the given elements, in order.
111    *
112    * @throws NullPointerException if any element is null
113    * @since 6.0 (source-compatible since 2.0)
114    */
115   @SuppressWarnings("unchecked") //
116   public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5) {
117     return copyOfInternal(e1, e2, e3, e4, e5);
118   }
119 
120   /**
121    * Returns an immutable multiset containing the given elements, in order.
122    *
123    * @throws NullPointerException if any element is null
124    * @since 6.0 (source-compatible since 2.0)
125    */
126   @SuppressWarnings("unchecked") //
127   public static <E> ImmutableMultiset<E> of(
128       E e1, E e2, E e3, E e4, E e5, E e6, E... others) {
129     return new Builder<E>()
130         .add(e1)
131         .add(e2)
132         .add(e3)
133         .add(e4)
134         .add(e5)
135         .add(e6)
136         .add(others)
137         .build();
138   }
139 
140   /**
141    * Returns an immutable multiset containing the given elements.
142    *
143    * <p>The multiset is ordered by the first occurrence of each element. For
144    * example, {@code ImmutableMultiset.copyOf([2, 3, 1, 3])} yields a multiset
145    * with elements in the order {@code 2, 3, 3, 1}.
146    *
147    * @throws NullPointerException if any of {@code elements} is null
148    * @since 6.0
149    */
150   public static <E> ImmutableMultiset<E> copyOf(E[] elements) {
151     return copyOf(Arrays.asList(elements));
152   }
153 
154   /**
155    * Returns an immutable multiset containing the given elements.
156    *
157    * <p>The multiset is ordered by the first occurrence of each element. For
158    * example, {@code ImmutableMultiset.copyOf(Arrays.asList(2, 3, 1, 3))} yields
159    * a multiset with elements in the order {@code 2, 3, 3, 1}.
160    *
161    * <p>Despite the method name, this method attempts to avoid actually copying
162    * the data when it is safe to do so. The exact circumstances under which a
163    * copy will or will not be performed are undocumented and subject to change.
164    *
165    * <p><b>Note:</b> Despite what the method name suggests, if {@code elements}
166    * is an {@code ImmutableMultiset}, no copy will actually be performed, and
167    * the given multiset itself will be returned.
168    *
169    * @throws NullPointerException if any of {@code elements} is null
170    */
171   public static <E> ImmutableMultiset<E> copyOf(
172       Iterable<? extends E> elements) {
173     if (elements instanceof ImmutableMultiset) {
174       @SuppressWarnings("unchecked") // all supported methods are covariant
175       ImmutableMultiset<E> result = (ImmutableMultiset<E>) elements;
176       if (!result.isPartialView()) {
177         return result;
178       }
179     }
180 
181     Multiset<? extends E> multiset = (elements instanceof Multiset)
182         ? Multisets.cast(elements)
183         : LinkedHashMultiset.create(elements);
184 
185     return copyOfInternal(multiset);
186   }
187 
188   private static <E> ImmutableMultiset<E> copyOfInternal(E... elements) {
189     return copyOf(Arrays.asList(elements));
190   }
191 
192   private static <E> ImmutableMultiset<E> copyOfInternal(
193       Multiset<? extends E> multiset) {
194     return copyFromEntries(multiset.entrySet());
195   }
196 
197   static <E> ImmutableMultiset<E> copyFromEntries(
198       Collection<? extends Entry<? extends E>> entries) {
199     long size = 0;
200     ImmutableMap.Builder<E, Integer> builder = ImmutableMap.builder();
201     for (Entry<? extends E> entry : entries) {
202       int count = entry.getCount();
203       if (count > 0) {
204         // Since ImmutableMap.Builder throws an NPE if an element is null, no
205         // other null checks are needed.
206         builder.put(entry.getElement(), count);
207         size += count;
208       }
209     }
210 
211     if (size == 0) {
212       return of();
213     }
214     return new RegularImmutableMultiset<E>(
215         builder.build(), Ints.saturatedCast(size));
216   }
217 
218   /**
219    * Returns an immutable multiset containing the given elements.
220    *
221    * <p>The multiset is ordered by the first occurrence of each element. For
222    * example,
223    * {@code ImmutableMultiset.copyOf(Arrays.asList(2, 3, 1, 3).iterator())}
224    * yields a multiset with elements in the order {@code 2, 3, 3, 1}.
225    *
226    * @throws NullPointerException if any of {@code elements} is null
227    */
228   public static <E> ImmutableMultiset<E> copyOf(
229       Iterator<? extends E> elements) {
230     Multiset<E> multiset = LinkedHashMultiset.create();
231     Iterators.addAll(multiset, elements);
232     return copyOfInternal(multiset);
233   }
234 
235   ImmutableMultiset() {}
236 
237   @Override public UnmodifiableIterator<E> iterator() {
238     final Iterator<Entry<E>> entryIterator = entrySet().iterator();
239     return new UnmodifiableIterator<E>() {
240       int remaining;
241       E element;
242 
243       @Override
244       public boolean hasNext() {
245         return (remaining > 0) || entryIterator.hasNext();
246       }
247 
248       @Override
249       public E next() {
250         if (remaining <= 0) {
251           Entry<E> entry = entryIterator.next();
252           element = entry.getElement();
253           remaining = entry.getCount();
254         }
255         remaining--;
256         return element;
257       }
258     };
259   }
260 
261   @Override
262   public boolean contains(@Nullable Object object) {
263     return count(object) > 0;
264   }
265 
266   @Override
267   public boolean containsAll(Collection<?> targets) {
268     return elementSet().containsAll(targets);
269   }
270 
271   /**
272    * Guaranteed to throw an exception and leave the collection unmodified.
273    *
274    * @throws UnsupportedOperationException always
275    * @deprecated Unsupported operation.
276    */
277   @Deprecated
278   @Override
279   public final int add(E element, int occurrences) {
280     throw new UnsupportedOperationException();
281   }
282 
283   /**
284    * Guaranteed to throw an exception and leave the collection unmodified.
285    *
286    * @throws UnsupportedOperationException always
287    * @deprecated Unsupported operation.
288    */
289   @Deprecated
290   @Override
291   public final int remove(Object element, int occurrences) {
292     throw new UnsupportedOperationException();
293   }
294 
295   /**
296    * Guaranteed to throw an exception and leave the collection unmodified.
297    *
298    * @throws UnsupportedOperationException always
299    * @deprecated Unsupported operation.
300    */
301   @Deprecated
302   @Override
303   public final int setCount(E element, int count) {
304     throw new UnsupportedOperationException();
305   }
306 
307   /**
308    * Guaranteed to throw an exception and leave the collection unmodified.
309    *
310    * @throws UnsupportedOperationException always
311    * @deprecated Unsupported operation.
312    */
313   @Deprecated
314   @Override
315   public final boolean setCount(E element, int oldCount, int newCount) {
316     throw new UnsupportedOperationException();
317   }
318 
319   @Override public boolean equals(@Nullable Object object) {
320     return Multisets.equalsImpl(this, object);
321   }
322 
323   @Override public int hashCode() {
324     return Sets.hashCodeImpl(entrySet());
325   }
326 
327   @Override public String toString() {
328     return entrySet().toString();
329   }
330 
331   private transient ImmutableSet<Entry<E>> entrySet;
332 
333   @Override
334   public ImmutableSet<Entry<E>> entrySet() {
335     ImmutableSet<Entry<E>> es = entrySet;
336     return (es == null) ? (entrySet = createEntrySet()) : es;
337   }
338 
339   private final ImmutableSet<Entry<E>> createEntrySet() {
340     return isEmpty() ? ImmutableSet.<Entry<E>>of() : new EntrySet();
341   }
342 
343   abstract Entry<E> getEntry(int index);
344 
345   private final class EntrySet extends ImmutableSet<Entry<E>> {
346     @Override
347     boolean isPartialView() {
348       return ImmutableMultiset.this.isPartialView();
349     }
350 
351     @Override
352     public UnmodifiableIterator<Entry<E>> iterator() {
353       return asList().iterator();
354     }
355 
356     @Override
357     ImmutableList<Entry<E>> createAsList() {
358       return new ImmutableAsList<Entry<E>>() {
359         @Override
360         public Entry<E> get(int index) {
361           return getEntry(index);
362         }
363 
364         @Override
365         ImmutableCollection<Entry<E>> delegateCollection() {
366           return EntrySet.this;
367         }
368       };
369     }
370 
371     @Override
372     public int size() {
373       return elementSet().size();
374     }
375 
376     @Override
377     public boolean contains(Object o) {
378       if (o instanceof Entry) {
379         Entry<?> entry = (Entry<?>) o;
380         if (entry.getCount() <= 0) {
381           return false;
382         }
383         int count = count(entry.getElement());
384         return count == entry.getCount();
385       }
386       return false;
387     }
388 
389     @Override
390     public int hashCode() {
391       return ImmutableMultiset.this.hashCode();
392     }
393 
394     // We can't label this with @Override, because it doesn't override anything
395     // in the GWT emulated version.
396     // TODO(cpovirk): try making all copies of this method @GwtIncompatible instead
397     Object writeReplace() {
398       return new EntrySetSerializedForm<E>(ImmutableMultiset.this);
399     }
400 
401     private static final long serialVersionUID = 0;
402   }
403 
404   static class EntrySetSerializedForm<E> implements Serializable {
405     final ImmutableMultiset<E> multiset;
406 
407     EntrySetSerializedForm(ImmutableMultiset<E> multiset) {
408       this.multiset = multiset;
409     }
410 
411     Object readResolve() {
412       return multiset.entrySet();
413     }
414   }
415 
416   private static class SerializedForm implements Serializable {
417     final Object[] elements;
418     final int[] counts;
419 
420     SerializedForm(Multiset<?> multiset) {
421       int distinct = multiset.entrySet().size();
422       elements = new Object[distinct];
423       counts = new int[distinct];
424       int i = 0;
425       for (Entry<?> entry : multiset.entrySet()) {
426         elements[i] = entry.getElement();
427         counts[i] = entry.getCount();
428         i++;
429       }
430     }
431 
432     Object readResolve() {
433       LinkedHashMultiset<Object> multiset =
434           LinkedHashMultiset.create(elements.length);
435       for (int i = 0; i < elements.length; i++) {
436         multiset.add(elements[i], counts[i]);
437       }
438       return ImmutableMultiset.copyOf(multiset);
439     }
440 
441     private static final long serialVersionUID = 0;
442   }
443 
444   // We can't label this with @Override, because it doesn't override anything
445   // in the GWT emulated version.
446   Object writeReplace() {
447     return new SerializedForm(this);
448   }
449 
450   /**
451    * Returns a new builder. The generated builder is equivalent to the builder
452    * created by the {@link Builder} constructor.
453    */
454   public static <E> Builder<E> builder() {
455     return new Builder<E>();
456   }
457 
458   /**
459    * A builder for creating immutable multiset instances, especially {@code
460    * public static final} multisets ("constant multisets"). Example:
461    * <pre> {@code
462    *
463    *   public static final ImmutableMultiset<Bean> BEANS =
464    *       new ImmutableMultiset.Builder<Bean>()
465    *           .addCopies(Bean.COCOA, 4)
466    *           .addCopies(Bean.GARDEN, 6)
467    *           .addCopies(Bean.RED, 8)
468    *           .addCopies(Bean.BLACK_EYED, 10)
469    *           .build();}</pre>
470    *
471    * <p>Builder instances can be reused; it is safe to call {@link #build} multiple
472    * times to build multiple multisets in series.
473    *
474    * @since 2.0 (imported from Google Collections Library)
475    */
476   public static class Builder<E> extends ImmutableCollection.Builder<E> {
477     final Multiset<E> contents;
478 
479     /**
480      * Creates a new builder. The returned builder is equivalent to the builder
481      * generated by {@link ImmutableMultiset#builder}.
482      */
483     public Builder() {
484       this(LinkedHashMultiset.<E>create());
485     }
486 
487     Builder(Multiset<E> contents) {
488       this.contents = contents;
489     }
490 
491     /**
492      * Adds {@code element} to the {@code ImmutableMultiset}.
493      *
494      * @param element the element to add
495      * @return this {@code Builder} object
496      * @throws NullPointerException if {@code element} is null
497      */
498     @Override public Builder<E> add(E element) {
499       contents.add(checkNotNull(element));
500       return this;
501     }
502 
503     /**
504      * Adds a number of occurrences of an element to this {@code
505      * ImmutableMultiset}.
506      *
507      * @param element the element to add
508      * @param occurrences the number of occurrences of the element to add. May
509      *     be zero, in which case no change will be made.
510      * @return this {@code Builder} object
511      * @throws NullPointerException if {@code element} is null
512      * @throws IllegalArgumentException if {@code occurrences} is negative, or
513      *     if this operation would result in more than {@link Integer#MAX_VALUE}
514      *     occurrences of the element
515      */
516     public Builder<E> addCopies(E element, int occurrences) {
517       contents.add(checkNotNull(element), occurrences);
518       return this;
519     }
520 
521     /**
522      * Adds or removes the necessary occurrences of an element such that the
523      * element attains the desired count.
524      *
525      * @param element the element to add or remove occurrences of
526      * @param count the desired count of the element in this multiset
527      * @return this {@code Builder} object
528      * @throws NullPointerException if {@code element} is null
529      * @throws IllegalArgumentException if {@code count} is negative
530      */
531     public Builder<E> setCount(E element, int count) {
532       contents.setCount(checkNotNull(element), count);
533       return this;
534     }
535 
536     /**
537      * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
538      *
539      * @param elements the elements to add
540      * @return this {@code Builder} object
541      * @throws NullPointerException if {@code elements} is null or contains a
542      *     null element
543      */
544     @Override public Builder<E> add(E... elements) {
545       super.add(elements);
546       return this;
547     }
548 
549     /**
550      * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
551      *
552      * @param elements the {@code Iterable} to add to the {@code
553      *     ImmutableMultiset}
554      * @return this {@code Builder} object
555      * @throws NullPointerException if {@code elements} is null or contains a
556      *     null element
557      */
558     @Override public Builder<E> addAll(Iterable<? extends E> elements) {
559       if (elements instanceof Multiset) {
560         Multiset<? extends E> multiset = Multisets.cast(elements);
561         for (Entry<? extends E> entry : multiset.entrySet()) {
562           addCopies(entry.getElement(), entry.getCount());
563         }
564       } else {
565         super.addAll(elements);
566       }
567       return this;
568     }
569 
570     /**
571      * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
572      *
573      * @param elements the elements to add to the {@code ImmutableMultiset}
574      * @return this {@code Builder} object
575      * @throws NullPointerException if {@code elements} is null or contains a
576      *     null element
577      */
578     @Override public Builder<E> addAll(Iterator<? extends E> elements) {
579       super.addAll(elements);
580       return this;
581     }
582 
583     /**
584      * Returns a newly-created {@code ImmutableMultiset} based on the contents
585      * of the {@code Builder}.
586      */
587     @Override public ImmutableMultiset<E> build() {
588       return copyOf(contents);
589     }
590   }
591 }